package edu.gatech.oad.mule;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import edu.gatech.oad.mule.input.DummyControllerOwner;
import edu.gatech.oad.mule.input.Input;
import edu.gatech.oad.mule.input.interfaces.ControllerOwner;
import edu.gatech.oad.mule.input.interfaces.InputListener;
import edu.gatech.oad.mule.input.interfaces.ControllerOwner.InputDevice;
import edu.gatech.oad.mule.model.GameDriver;
import edu.gatech.oad.mule.model.NewGameDriver;
import edu.gatech.oad.mule.model.interfaces.GameStateMachineListener;
import edu.gatech.oad.mule.view.View;
import edu.gatech.oad.mule.view.interfaces.RenderableGameDriver;
import edu.gatech.oad.mule.config.PlayerData;
import edu.gatech.oad.mule.config.interfaces._ConfigurationPanel;

/**
 * This class is the main GUI for the configuration screen. It will handle
 * gathering and validating user input, and pass the result over to GameDriver
 * to start setting up the game.
 * 
 * @author Brendan McGarry, Dale Avery, D'Miria Collins, Natasha Parekh, Jaemin
 *         Park
 * @version %I%, %G%
 */
public class ConfigurationPanel extends JPanel implements _ConfigurationPanel {

	// maximum and minimum number of players allowed
	public static final int MAX_PLAYERS = 4;
	public static final int MIN_PLAYERS = 2;

	// possible races
	public static final String[] RACES = { "Human", "Flapper", "Bonzoid",
			"Ugaite", "Buzzite" };

	// possible input devices
	public static final String[] INPUT_DEVICES = { "Keyboard", "Gamepad" };

	// all panels must have this amount of padding around them
	public static final int PANEL_PADDING = 10;

	public static final String[] DIFFICULTY = { "Beginner", "Standard",
			"Tournament" };

	public static final String[] MAP = { "Standard", "Random" };

	// the subpanels
	private UpperConfigPanel top;
	private MiddleConfigPanel middle;
	private LowerConfigPanel bottom;

	// instance of gamedriver
	NewGameDriver gd;

	/**
	 * This constructor sets up the configuration GUI so the players can set up
	 * a new game and start playing
	 * 
	 * @param panelSize
	 *            The size of the GUI
	 */
	public ConfigurationPanel() {
		super();

		System.out.println("configurationPanel");

		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

		// create/add the upper panel
		top = new UpperConfigPanel(this);
		add(top);

		// create/add the middle panel
		middle = new MiddleConfigPanel(this);
		add(middle);

		// create/add the lower panel
		bottom = new LowerConfigPanel(this);
		add(bottom);

		System.out.println("/configurationPanel");
	}// end constructor

	/**
	 * This method disables the user from editing some of the components in the
	 * middle panel
	 * 
	 * @param howMany
	 *            The number of components to enable
	 */
	public void enableComponents(int howMany) {
		System.out.println("enableComponents");
		System.out.println("/enableComponents");
		middle.enableComponents(howMany);
	}// end method disableComponents

	/**
	 * @return The level of difficulty currently selected
	 */
	public String getDifficulty() {
		System.out.println("getDifficulty");
		System.out.println("/getDifficulty");
		return top.getDifficulty();
	}// end method getDIfficulty

	/**
	 * @return The type of map selected
	 */
	public String getMap() {
		System.out.println("getMap");
		System.out.println("/getMap");
		return top.getMap();
	}// end method getDIfficulty

	/**
	 * @return The number of players currently selected
	 */
	public int getNumberOfPlayers() {
		System.out.println("getNumberOfPlayers");
		System.out.println("/getNumberOfPlayers");
		return top.getNumberOfPlayers();
	}// end method getNumberOfPlayers

	/**
	 * @return An array of the player's configuration data (color, name, etc).
	 *         row 0 corresponds to the player 1's data, row 1 corresponds to
	 *         player 2's data, etc. The table structure looks like this:
	 * 
	 *         p1.playerData = p1.name p1.race p1.color p1.input_device
	 *         p1.playerData = p2.name p2.race p2.color p2.input_device
	 *         p1.playerData = p3.name p3.race p3.color p3.input_device ... ...
	 *         ... ... pn.playerData = pn.name pn.race pn.color pn.input_device
	 * 
	 */
	public PlayerData[] getPlayerData() {
		System.out.println("getPlayerData");
		System.out.println("/getPlayerData");
		return middle.getPlayerData(getNumberOfPlayers());
	}// end method getDIfficulty

	/**
	 * @param whichPlayer
	 *            An integer value that corresponds to the desired player's
	 *            data. 0 corresponds to the first player, 1 corresponds to the
	 *            second player, 1 corresponds to the third, etc
	 * 
	 * @return a specific player's configuration data.
	 */
	public PlayerData getPlayerData(int whichPlayer) {
		System.out.println(whichPlayer);
		System.out.println("getPlayerData(whichPlayer)");
		System.out.println("/getPlayerData(whichPlayer)");
		return (middle.getPlayerData(getNumberOfPlayers()))[whichPlayer];
	}// end method getDIfficulty

	/**
	 * This method validates the user input. If all the input checks out, then
	 * it passes all the data over to GameDriver to start building the game
	 */
	public void validateInput() {
		System.out.println("validateInput");
		if (middle.validateInput(getNumberOfPlayers())) {
			System.out.println("valid");
			// make the game driver to start the game
			runNewGame(this);
		}// end if
		System.out.println("/validateInput");
	}// end method validate

	/**
	 * Builds necessary object to start a game of Mule.
	 * 
	 * @param panel The configuration information for the game.
	 */
	private void runNewGame(_ConfigurationPanel panel) {
		// Build a JFrame for the game.
		JFrame frame = createFrame();
		frame.setVisible(true);
		//create a new gamedriver object based on the settings
		//  players have chosen in the configuration menu
		gd = new NewGameDriver(panel);
		//configure the listeners
		setupGameDriver(gd, frame);
		//start the gamedriver
		gd.start();
		//hide this GUI so we can't have duplicate games
		hideConfigGUI();
	}//end method runNewGame

	/**
	 * Resumes a saved game.
	 * 
	 * @param file A serialized GameDriver file.
	 */
	public void runSavedGame(File file) {
		// Build a JFrame for the game.
		JFrame frame = createFrame();
		frame.setVisible(true);
		try {
			//attempt to load a gamedriver
			//  from a serialized one
			gd = NewGameDriver.load(file);
			setupGameDriver(gd, frame);
			gd.start();
			hideConfigGUI();
		}//end try
		catch (Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}//end catch
	}//end method runSavedGame

	/**
	 * @return A JFrame with appropriate menus.
	 */
	public JFrame createFrame() {
		//create a new JFrame
		JFrame frame = new JFrame();
		//create a new menu bar for the top of the game
		JMenuBar menu = new JMenuBar();
		//file option for the menu
		JMenu file = new JMenu("File");
		//file > save option
		JMenuItem save = new JMenuItem("Save");
		//add a listener to the save button
		save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				//call the save method inside game driver to
				//  serialize/save it
				System.out.println("Save selected.");
				gd.save();
			}//end method actionPerformed
		});
		//file > exit option
		JMenuItem exit = new JMenuItem("Exit");
		//add listener to the exit button
		exit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				//bye-bye
				System.exit(0);
			}//end method actionPerformed
		});
		//add the file option to the menu
		menu.add(file);
		//add save/exit option to file
		file.add(save);
		file.add(exit);
		//add the menu to the JFrame
		frame.setJMenuBar(menu);
		//pack it
		frame.pack();
		//close when click the red x
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		return frame;
	}//end method createFrame
	
	/**
	 * Associates a GameDriver, JFrame, and input objects together;
	 * Sets up listeners, etc
	 * 
	 * @param gameDriver A GameDriver object (either new, or deserialized)
	 * @param frame The frame that holds all the GUI components of the game
	 */
	private void setupGameDriver(NewGameDriver gameDriver, JFrame frame) {
		//activate the listener and associate it with the game's GUI
		GameStateMachineListener input = new Input(gameDriver, frame);
		gameDriver.addActionListener(input);
		((Input) input).addActionListener((InputListener) gameDriver);
		GameStateMachineListener view = new View(frame,
				(RenderableGameDriver) gameDriver);
		gameDriver.addActionListener(view);
	}//end method setupGameDriver
	
	/**
	 * This method hides the configuration GUI after the user's
	 * start a game
	 */
	private void hideConfigGUI(){
		// get the root JFrame (the JFrame that is holding this
		// JPanel) and make it invisible. This effectively
		// "closes" the JFrame
		JFrame root = (JFrame) SwingUtilities.getRoot(this);
		root.setVisible(false);
	}//end method hideConfigGUI

}// end class ConfigurationPanel
